package com.gitee.huanminabc.utils_tools.redis.aspect.handler;

import com.alibaba.fastjson.JSON;
import com.gitee.huanminabc.utils_tools.redis.aspect.note.RedisCache;
import com.gitee.huanminabc.utils_tools.redis.redisTemplate.utils.RedisTemplateUtil;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 给方法加上缓存, 会根据类的全路径名和方法名以及参数生成一个key, 作为缓存的key 也可以自定义key
 * 方法参数会先转换为JSON进行拼接到key中,这样才能保证唯一性, 如果方法参数内容太多, 可能会导致key过长
 * 缓存的是方法返回值
 * @author huanmin
 * @date 2024/2/1
 */
@Aspect
@Component
public class RedisCacheAspect {
    @Autowired
    private RedisTemplateUtil redisTemplateUtil;

    @Pointcut("@annotation(redisCache)")
    public void redisCacheAspect(RedisCache redisCache) {
    }

    @Around(value = "redisCacheAspect(redisCache)", argNames = "pjp,redisCache")
    public Object around(ProceedingJoinPoint pjp, RedisCache redisCache) throws Throwable {
        String value = redisCache.value();
        if (Strings.isBlank(value)) {
            //获取类.方法名
            String className = pjp.getTarget().getClass().getName();
            String methodName = pjp.getSignature().getName();
            value = className + "::" + methodName + "::" + JSON.toJSONString(pjp.getArgs());
        }
        Object proceed;//底层会自动序列化
        try {
            RedisSwitchAspect.dbIndex(redisCache.dbIndex()); //切换数据源
            Object res = redisTemplateUtil.get(value);//获取缓存
            if (res != null) {
                Class<?> returnType = ((MethodSignature) pjp.getSignature()).getMethod().getReturnType();
                return JSON.parseObject(res.toString(), returnType);
            }
            proceed = pjp.proceed();

            redisTemplateUtil.set(value, JSON.toJSONString(proceed), redisCache.expire());//设置缓存
        } finally {
            RedisSwitchAspect.cleanThreadLocal();//还原数据源索引
        }
        return proceed;
    }
}
